package layers.physical;

import java.util.ArrayList;
import java.util.HashMap;

import layers.agents.PackageAgent;
import layers.ants.AntTickListener;
import layers.ants.Mark;
import layers.ants.PheromoneStack;
import events.antEvents.MarkCreatedEvent;
import events.antEvents.MarkDestroyedEvent;
import events.antEvents.PheromonesDroppedEvent;
import events.roadEvents.CrossRoadsCreatedEvent;
import framework.events.EventBroker;
import framework.layer.physical.connections.Connector;
import framework.layer.physical.position.ConnectorPosition;

public class Crossroad extends Connector<Truck, Crossroad, Road> implements AntTickListener{

	public static final int CAPACITY = Integer.MAX_VALUE;
	private PackageAgent packageAgent;
	private HashMap<Road, PheromoneStack> packageAntPheromoneStacks;
	private ArrayList<Mark> explorationAntMarks = new ArrayList<Mark>();
	private ArrayList<Mark> deliveryAntMarks = new ArrayList<Mark>();

	public Crossroad() {
		super(CAPACITY, CAPACITY);
		this.packageAntPheromoneStacks = new HashMap<Road, PheromoneStack>();
		EventBroker.getEventBroker().notifyAll(new CrossRoadsCreatedEvent(this));
	}

	@Override
	protected boolean canEnterOffroad() {
		return false;
	}

	@Override
	protected boolean canEnterOnroad() {
		return true;
	}

	@Override
	protected boolean canLeaveOffroad() {
		return false;
	}

	@Override
	protected boolean canLeaveOnroad() {
		return true;
	}

	@Override
	protected void executeSpecificDeploymentOptions(Truck connectionEntity,
			ConnectorPosition<Truck, Crossroad, Road> pos) {
	}

	@Override
	protected void onEnterOffroad(Truck connectionEntity, Road fromConnection) {
	}

	@Override
	protected void onEnterOnroad(Truck connectionEntity, Road fromConnection) {
	}

	@Override
	protected void onLeaveOffroad(Truck connectionEntity, Road toConnection) {
	}

	@Override
	protected void onLeaveOnroad(Truck connectionEntity, Road toConnection) {
	}

	public void recievePackage(PackageAgent agent) {
		if(agent == null)
			throw new IllegalArgumentException("Agent cannot be null.");
		if(!hasPackage()){
			this.packageAgent = agent;
		}
	}

	public boolean hasPackage(){
		return this.packageAgent != null;
	}

	public PackageAgent getPackageAgent(){
		return this.packageAgent;
	}

	private HashMap<Road, PheromoneStack> getPackageAntPheromoneStack() {
		return this.packageAntPheromoneStacks;
	}

	public void dropPackageAntPheromones(Road road, long value){
		if(road != null){
			this.packageAntPheromoneStacks.get(road).dropPheromones(value);
			EventBroker.getEventBroker().notifyAll(new PheromonesDroppedEvent(this.getPosition()));
		}
	}

	public void dropExplorationAntMark(Mark mark) {
		if(this.explorationAntMarks.isEmpty() && this.deliveryAntMarks.isEmpty())
			EventBroker.getEventBroker().notifyAll(new MarkCreatedEvent(this.getId(), this.getPosition(), this));
		Mark toRemove = null;
		for(Mark mo : this.explorationAntMarks)
			if(mo.getTruckId() == mark.getTruckId())
				toRemove = mo;
		removeExplorationMark(toRemove);
		this.explorationAntMarks.add(mark);
		
	}

	public Road getRoadWithMostPheromones() {
		Road result = null;
		for(Road road : this.getOutgoingConnections()){
			if(this.getPackageAntPheromoneStack().get(road).getValue() > 1){
				if(result == null)
					result = road;
				if(this.getPackageAntPheromoneStack().get(road).getValue() > this.getPackageAntPheromoneStack().get(result).getValue()){
					result = road;
				}
			}
		}
		return result;
	}

	public boolean hasMoreOrEqualAmountOfPheromonesThen(long pheromonesToDrop) {
		boolean result = false;
		for(Road road : getOutgoingConnections()){
			if(this.getPackageAntPheromoneStack().get(road).getValue() >= pheromonesToDrop)
				result = true;
		}				
		return result;
	}

	@Override
	protected void outgoingConnectionAdded(Road road) {
		this.packageAntPheromoneStacks.put(road, new PheromoneStack(0));
	}

	@Override
	public void processAntTick() {
		updateMarks();
	}

	private void updateMarks() {
		ArrayList<Mark> toBeRemoved = new ArrayList<Mark>();
		for(Mark mark : this.explorationAntMarks){
			mark.decreaseTimeToLive();
			if(mark.isDead())
				toBeRemoved.add(mark);
		}
		for(Mark mark : toBeRemoved){
			this.explorationAntMarks.remove(mark);
		}
		toBeRemoved.clear();
		
		for(Mark mark : this.deliveryAntMarks){
			mark.decreaseTimeToLive();
			if(mark.isDead())
				toBeRemoved.add(mark);
		}
		for(Mark mark : toBeRemoved){
			this.deliveryAntMarks.remove(mark);
		}
		if(this.deliveryAntMarks.isEmpty() && this.explorationAntMarks.isEmpty())
			EventBroker.getEventBroker().notifyAll(new MarkDestroyedEvent(this.getId(), this.getPosition(), this));
	}

	public void removePackage() {
		this.packageAgent = null;
	}

	public boolean containsBetterExplorationMark(int truckId, long traveledDistance) {
		for(Mark mark : this.explorationAntMarks){
			if(mark.getTruckId() == truckId)
				if(mark.hasDistanceLowerThan(traveledDistance)){
					return true;
				}
		}
		return false;
	}

	public boolean containsBetterDeliveryMark(int truckId, long traveledDistance) {
		for(Mark mark : this.deliveryAntMarks){
			if(mark.getTruckId() == truckId)
				if(mark.hasDistanceLowerThan(traveledDistance)){
					return true;
				}
		}
		return false;
	}

	private void removeExplorationMark(Mark mark) {
		this.explorationAntMarks.remove(mark);		
	}
	
	private void removeDeliveryMark(Mark mark) {
		this.explorationAntMarks.remove(mark);		
	}


	public void dropDeliveryAntMark(Mark mark) {
		if(this.deliveryAntMarks.isEmpty() && this.explorationAntMarks.isEmpty())
			EventBroker.getEventBroker().notifyAll(new MarkCreatedEvent(this.getId(), this.getPosition(), this));
		Mark toRemove = null;
		for(Mark mo : this.deliveryAntMarks)
			if(mo.getTruckId() == mark.getTruckId())
				toRemove = mo;
			removeDeliveryMark(toRemove);
		this.deliveryAntMarks.add(mark);

	}
}


